﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Collections.Specialized;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml.Serialization;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;
using System.Xml;
using System.Collections;
using System.Text;

namespace SharingCenter.Web.Classes
{
    public class Utility
    {
        public static class ImageUtility
        {
            public static void ResizeImageByWidthOnly(int MaxSideSize, Stream Buffer, out Bitmap bmpResized)
            {
                int intNewWidth = 0;
                int intNewHeight = 0;
                using (Image imgInput = Image.FromStream(Buffer))
                {
                    //Determine image format
                    ImageFormat fmtImageFormat = imgInput.RawFormat;

                    //get image original width and height
                    int intOldWidth = imgInput.Width;
                    int intOldHeight = imgInput.Height;

                    //determine if landscape or portrait
                    int intMaxSide = intOldWidth;
                    int widthOffset = 0, heightOffset = 0;

                    if (intMaxSide > MaxSideSize)
                    {
                        //set new width and height
                        double dblCoef = MaxSideSize / (double)intMaxSide;
                        intNewWidth = Convert.ToInt32(dblCoef * intOldWidth);
                        intNewHeight = Convert.ToInt32(dblCoef * intOldHeight);
                    }
                    else
                    {
                        intNewWidth = intOldWidth;
                        intNewHeight = intOldHeight;
                    }

                    //create new bitmap

                    bmpResized = new Bitmap(intNewWidth, intNewHeight);
                    using (Graphics g = Graphics.FromImage(bmpResized))
                    {
                        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                        g.FillRectangle(Brushes.White, 0, 0, intNewWidth, intNewHeight);
                        g.DrawImage(imgInput, widthOffset, heightOffset, intNewWidth + 2, intNewHeight + 2);
                    }
                }

            }

            public static void ResizeImage(int MaxSideSize, Stream Buffer, out Bitmap bmpResized)
            {
                int intNewWidth = 0;
                int intNewHeight = 0;
                using (Image imgInput = Image.FromStream(Buffer))
                {
                    //Determine image format
                    ImageFormat fmtImageFormat = imgInput.RawFormat;

                    //get image original width and height
                    int intOldWidth = imgInput.Width;
                    int intOldHeight = imgInput.Height;

                    //determine if landscape or portrait
                    int intMaxSide;
                    int widthOffset = 0, heightOffset = 0;

                    if (intOldWidth >= intOldHeight)
                    {
                        intMaxSide = intOldWidth;
                    }
                    else
                    {
                        widthOffset = -1;
                        intMaxSide = intOldHeight;
                    }

                    

                    if (intMaxSide > MaxSideSize)
                    {
                        //set new width and height
                        double dblCoef = MaxSideSize / (double)intMaxSide;
                        intNewWidth = Convert.ToInt32(dblCoef * intOldWidth);
                        intNewHeight = Convert.ToInt32(dblCoef * intOldHeight);
                    }
                    else
                    {
                        intNewWidth = intOldWidth;
                        intNewHeight = intOldHeight;
                    }

                    //create new bitmap

                    bmpResized = new Bitmap(intNewWidth, intNewHeight);
                    using (Graphics g = Graphics.FromImage(bmpResized))
                    {
                        g.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.HighQuality;
                        g.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                        g.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                        g.FillRectangle(Brushes.White, 0, 0, intNewWidth, intNewHeight);
                        g.DrawImage(imgInput, widthOffset, heightOffset, intNewWidth + 2, intNewHeight + 2);
                    }
                }
                    
            }
        }

        public static class Icons
        {
            
        }

        public class TwoLayerCascade
        {
            XDocument xdoc;
            string xmlPath;

            public TwoLayerCascade(string mappedXmlPath)
            {
                xmlPath = mappedXmlPath;
                xdoc = XDocument.Load(mappedXmlPath);
            }

            public string AddCategory(string category)
            {
                Guid newGuid = Guid.NewGuid();
                XElement c = xdoc.XPathSelectElement(string.Format("Categories/Category[@Name='{0}']", category));
                if (c == null)
                {
                    c = new XElement("Category", new XAttribute("Name", category),
                        new XAttribute("Guid", newGuid)
                        );
                    xdoc.Root.Add(c);
                }
                xdoc.Save(xmlPath);
                return newGuid.ToString();
            }

            public void AddItemUnderCategory(string category, string item)
            {
                XElement c = xdoc.XPathSelectElement(string.Format("Categories/Category[@Guid='{0}']", category));
                if (c== null)
                {
                    c= new XElement("Category", new XAttribute("Name", category),
                        new XAttribute("Guid", Guid.NewGuid())
                        );
                    xdoc.Root.Add(c);
                }
                

                c.Add(new XElement("Item", new XAttribute("Name", item),
                    new XAttribute("Guid", Guid.NewGuid())
                    ));
                xdoc.Save(xmlPath);
            }

            public void DeleteItem(string guid)
            {
                xdoc.XPathSelectElement(string.Format("//Item[@Guid='{0}']", guid)).Remove();
                xdoc.Save(xmlPath);
            }

            public void DeleteCategory(string guid)
            {
                xdoc.XPathSelectElement(string.Format("//Category[@Guid='{0}']", guid)).Remove();
                xdoc.Save(xmlPath);
            }

            public void RenameCategory(string guid, string newName)
            {
                xdoc.XPathSelectElement(string.Format("//Category[@Guid='{0}']", guid)).Attribute("Name").Value = newName;
                xdoc.Save(xmlPath);
            }
        }

        public static string GetGenderName(enums.Gender gender)
        {
            switch (gender)
            {
                case SharingCenter.Web.Classes.enums.Gender.Male:
                    return "男";
                case SharingCenter.Web.Classes.enums.Gender.Female:
                    return "女";
                default:
                    return "未知";
            }
        }

        public static string GetGenderName(string namedGender)
        {
            enums.Gender gender = (enums.Gender)Enum.Parse(typeof(enums.Gender), namedGender);
            switch (gender)
            {
                case SharingCenter.Web.Classes.enums.Gender.Male:
                    return "男";
                case SharingCenter.Web.Classes.enums.Gender.Female:
                    return "女";
                default:
                    return "未知";
            }
        }

        public static string GetTa(enums.Gender gender)
        {
            switch (gender)
            {
                case SharingCenter.Web.Classes.enums.Gender.Male:
                    return "他";
                case SharingCenter.Web.Classes.enums.Gender.Female:
                    return "她";
                default:
                    return "TA";
            }
        }

        public static string FriendlyDateTime(object d, TimeDisplayEnum timeDisplay, bool showYear)
        {
            DateTime date;
            try
            {
                date = Convert.ToDateTime(d);
            }
            
            catch { return ""; }

            if(!WebConsts.IS_LOCAL && (timeDisplay == TimeDisplayEnum.LiteralHours || timeDisplay == TimeDisplayEnum.LiteralHoursNoTimeInOtherDays))
                date += WebConsts.WEBSERVER_OFFSET;

            string ret = date.ToString(showYear ? WebConsts.DATE_FORMAT : WebConsts.DATE_FORMAT_NO_YEAR) + " ";

            int days = (int)(date.Date - DateTime.Today).TotalDays;
            double hours = (date - DateTime.Now).TotalHours;

            switch (days)
            {
                case 0:
                    ret = "今天 ";
                    break;
                case 1:
                    ret = "明天 ";
                    break;
                case 2:
                    ret = "后天 ";
                    break;
                case -1:
                    ret = "昨天 ";
                    break;
                case -2:
                    ret = "前天 ";
                    break;
                default: break;
            }

            if ((timeDisplay == TimeDisplayEnum.LiteralHours || timeDisplay == TimeDisplayEnum.LiteralHoursNoTimeInOtherDays) && Math.Abs(hours) <= 24)
            {
                ret = "";
            }

            switch (timeDisplay)
            {
                case TimeDisplayEnum.AmPmOnly:
                    ret += date.ToString("tt");
                    break;
                case TimeDisplayEnum.HourMin:
                    ret += date.ToString("HH:mm");
                    break;
                case TimeDisplayEnum.FullTime:
                    ret += date.ToShortTimeString();
                    break;
                case TimeDisplayEnum.LiteralHours:
                    ret = Utility.ShowLiteralTimeSpan(ret, date);
                    break;
                case TimeDisplayEnum.LiteralHoursNoTimeInOtherDays:
                    if (Math.Abs(days) <= 2)
                    {
                        ret = Utility.ShowLiteralTimeSpan(ret, date);
                    }
                    break;
                default: break;
            }

            return ret;
        }

        public static string ShowLiteralTimeSpan(string date, DateTime dateTimeToCompare)
        {
            TimeSpan span;
            string ret = "";

            if (DateTime.Now >= dateTimeToCompare)
            {
                span = DateTime.Now - dateTimeToCompare;

                // past
                if (span.TotalSeconds < 20)
                {
                    ret = "几秒钟前";
                }
                else if (span.TotalSeconds < 60)
                {
                    ret = Math.Floor(span.TotalSeconds) + "秒钟前";
                }
                else if (span.TotalMinutes < 60)
                {
                    ret = Math.Floor(span.TotalMinutes) + "分钟前";
                }
                else if (span.TotalHours < 24)
                {
                    ret = Math.Floor(span.TotalHours) + "小时前";
                }
                else
                {
                    ret = date+dateTimeToCompare.ToString("HH:mm");
                }
            }
            else
            {
                // future
                span = dateTimeToCompare - DateTime.Now;
                if (span.TotalSeconds < 20)
                {
                    ret = "几秒钟后";
                }
                else if (span.TotalSeconds < 60)
                {
                    ret = Math.Floor(span.TotalSeconds) + "秒钟后";
                }
                else if (span.TotalMinutes < 60)
                {
                    ret = Math.Floor(span.TotalMinutes) + "分钟后";
                }
                else if (span.TotalHours < 24)
                {
                    ret = Math.Floor(span.TotalHours) + "小时后";
                }
                else
                {
                    ret = date+dateTimeToCompare.ToString("HH:mm");
                }
            }


            return ret;
        }

        public static string AddHashToQueryString(NameValueCollection queryString)
        {
            string hashKeyName = "_h";
            NameValueCollection t = new NameValueCollection();
            bool hasHashKey = false;
            foreach (string key in queryString.Keys)
            {
                hasHashKey = key == hashKeyName;
                t.Add(key, queryString[key]);
            }

            if (!hasHashKey)
            {
                t.Add(hashKeyName, Guid.NewGuid().ToString());
            }
            else
            {
                t[hashKeyName] = Guid.NewGuid().ToString();
            }

            List<string> ret = new List<string>();
            foreach (string key in t.Keys)
            {
                ret.Add(string.Format("{0}={1}", key, t[key]));
            }

            string r = string.Join("&", ret.ToArray());
            return r;
        }

        public static string SerializeSimpleList(List<object> list)
        {
            XElement root = new XElement("List");
            foreach (object obj in list)
            {
                XElement item = new XElement("Item",
                    new XAttribute("Type", obj.GetType().ToString()),
                    new XAttribute("Value", obj.ToString())
                    );

                root.Add(item);
            }
            return root.ToString();
        }

        public static XElement WrapContextXml(ref XElement x)
        {
            return new XElement("Context", x);
        }

        public static class XmlToJson
        {
            public static string XmlToJSON(XmlDocument xmlDoc)
            {
                StringBuilder sbJSON = new StringBuilder();
                sbJSON.Append("{ ");
                XmlToJSONnode(sbJSON, xmlDoc.DocumentElement, true);
                sbJSON.Append("}");
                return sbJSON.ToString();
            }

            //  XmlToJSONnode:  Output an XmlElement, possibly as part of a higher array
            private static void XmlToJSONnode(StringBuilder sbJSON, XmlElement node, bool showNodeName)
            {
                if (showNodeName)
                    sbJSON.Append("\"" + SafeJSON(node.Name) + "\": ");
                sbJSON.Append("{");
                // Build a sorted list of key-value pairs
                //  where   key is case-sensitive nodeName
                //          value is an ArrayList of string or XmlElement
                //  so that we know whether the nodeName is an array or not.
                SortedList childNodeNames = new SortedList();

                //  Add in all node attributes
                if (node.Attributes != null)
                    foreach (XmlAttribute attr in node.Attributes)
                        StoreChildNode(childNodeNames, attr.Name, attr.InnerText);

                //  Add in all nodes
                foreach (XmlNode cnode in node.ChildNodes)
                {
                    if (cnode is XmlText)
                        StoreChildNode(childNodeNames, "value", cnode.InnerText);
                    else if (cnode is XmlElement)
                        StoreChildNode(childNodeNames, cnode.Name, cnode);
                }

                // Now output all stored info
                foreach (string childname in childNodeNames.Keys)
                {
                    ArrayList alChild = (ArrayList)childNodeNames[childname];
                    if (alChild.Count == 1)
                        OutputNode(childname, alChild[0], sbJSON, true);
                    else
                    {
                        sbJSON.Append(" \"" + SafeJSON(childname) + "\": [ ");
                        foreach (object Child in alChild)
                            OutputNode(childname, Child, sbJSON, false);
                        sbJSON.Remove(sbJSON.Length - 2, 2);
                        sbJSON.Append(" ], ");
                    }
                }
                sbJSON.Remove(sbJSON.Length - 2, 2);
                sbJSON.Append(" }");
            }

            //  StoreChildNode: Store data associated with each nodeName
            //                  so that we know whether the nodeName is an array or not.
            private static void StoreChildNode(SortedList childNodeNames, string nodeName, object nodeValue)
            {
                // Pre-process contraction of XmlElement-s
                if (nodeValue is XmlElement)
                {
                    // Convert  <aa></aa> into "aa":null
                    //          <aa>xx</aa> into "aa":"xx"
                    XmlNode cnode = (XmlNode)nodeValue;
                    if (cnode.Attributes.Count == 0)
                    {
                        XmlNodeList children = cnode.ChildNodes;
                        if (children.Count == 0)
                            nodeValue = null;
                        else if (children.Count == 1 && (children[0] is XmlText))
                            nodeValue = ((XmlText)(children[0])).InnerText;
                    }
                }
                // Add nodeValue to ArrayList associated with each nodeName
                // If nodeName doesn't exist then add it
                object oValuesAL = childNodeNames[nodeName];
                ArrayList ValuesAL;
                if (oValuesAL == null)
                {
                    ValuesAL = new ArrayList();
                    childNodeNames[nodeName] = ValuesAL;
                }
                else
                    ValuesAL = (ArrayList)oValuesAL;
                ValuesAL.Add(nodeValue);
            }

            private static void OutputNode(string childname, object alChild, StringBuilder sbJSON, bool showNodeName)
            {
                if (alChild == null)
                {
                    if (showNodeName)
                        sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
                    sbJSON.Append("null");
                }
                else if (alChild is string)
                {
                    if (showNodeName)
                        sbJSON.Append("\"" + SafeJSON(childname) + "\": ");
                    string sChild = (string)alChild;
                    sChild = sChild.Trim();
                    sbJSON.Append("\"" + SafeJSON(sChild) + "\"");
                }
                else
                    XmlToJSONnode(sbJSON, (XmlElement)alChild, showNodeName);
                sbJSON.Append(", ");
            }

            // Make a string safe for JSON
            private static string SafeJSON(string sIn)
            {
                StringBuilder sbOut = new StringBuilder(sIn.Length);
                foreach (char ch in sIn)
                {
                    if (Char.IsControl(ch) || ch == '\'')
                    {
                        int ich = (int)ch;
                        sbOut.Append(@"\u" + ich.ToString("x4"));
                        continue;
                    }
                    else if (ch == '\"' || ch == '\\' || ch == '/')
                    {
                        sbOut.Append('\\');
                    }
                    sbOut.Append(ch);
                }
                return sbOut.ToString();
            }

        }
    }
}
